#include "Ship.h"
#include "BattleEncoded.h"
#include "BattleShip.h"
#include <iostream>

BattleShip::BattleShip()
{
	for (int i = 0; i < MAX_H; i++)
		for (int j = 0; j < MAX_V; j++)
		{
			m_table[i][j] = -1; 
		}

	for (int i=0;i < MAX_SHIP+1;i++)
		m_hasShip[i] = false;

	for (int i=0; i < MAX_H; i++)
	for (int j=0; j < MAX_V; j++)
	{
		m_left[i][j] = j;
		m_right[i][j] = 9 - j;
		m_top[i][j] = i;
		m_bottom[i][j] = 9 - i;
	}

	m_maxLength = Ship::GetLength(Ship::AIRCRAFT);
}

BattleShip::~BattleShip()
{
}

BattleShipPtr BattleShip::Clone()
{
	BattleShipPtr result(new BattleShip());
	for (int i = 0; i < MAX_H; i++)
		for (int j = 0; j < MAX_V; j++)
		{
			result->m_table[i][j] = m_table[i][j];
		}

	for (int i=0;i < MAX_SHIP+1;i++)
		result->m_hasShip[i] = m_hasShip[i];

	return result;
}

bool BattleShip::IsInBattle(Point p)
{
	if (p.first < 0) return false;
	if (p.first >= MAX_H) return false;
	if (p.second < 0) return false;
	if (p.second >= MAX_V) return false;
	return true;
}

bool BattleShip::IsInShip(Point p)
{
	return IsInBattle(p) && (m_table[p.first][p.second] == 1);
}

bool BattleShip::IsShipInBattle(ShipPtr _ship)
{
	PointSet ps = _ship->GetPos();
	PointSet::iterator ii;
	for (ii = ps.begin(); ii != ps.end(); ii++)
	{
		if (!IsInBattle(*ii))	
		{
			return false;
		}
	}
	return true;
}

bool BattleShip::PutShip(ShipPtr _ship)
{
	PointSet ps = _ship->GetSpaceForShip();
	PointSet::iterator ii;
	for (ii = ps.begin(); ii != ps.end(); ii++)
	{
		if (IsInShip(*ii))
		{
			return false;
		}
	}
	// check ship in the battle or not
	if (!IsShipInBattle(_ship))
	{
		return false;
	}
	//assign ship into battle
	ps = _ship->GetPos();
	for (ii = ps.begin(); ii != ps.end(); ii++)
	{
		m_table[ii->first][ii->second] = 1;
	}
	m_hasShip[_ship->GetType()] = true;
	return true;
}

//encode to int64
unsigned int BattleShip::EncodeInt32()
{
	int result = 0;
	int power = 0;
	for (int i = 0; i < MAX_H/2; i++)
		for (int j = 0; j < MAX_H/2; j++)
		{
			//std::cout << i <<":"<< j << std::endl;
			if (m_table[i][j] == 1)
			{
				result |= (1 << power);
				//std::cout << " Power: " << power << " : "<<result << std::endl;
			}
			power++;
		}
	
	for (int i=Ship::DESTROYER; i<=Ship::AIRCRAFT;i++)
	{
		if (m_hasShip[i])
		{
			//std::cout << "Got Type: "<<i<< std::endl;
			result |= (1 << power);
		}
		power++;
	}
		
	return result;
}
//End

//Decode a int32 to battle ship
BattleShipPtr BattleShip::DecodeInt32(unsigned int data)
{
	BattleShipPtr result(new BattleShip());
	int power = 0;
	for (int i = 0; i < MAX_H/2; i++)
		for (int j = 0; j < MAX_H/2; j++)
		{
			result->m_table[i][j] = (data & (1 << power)) == (1 << power);
			power++;
		}
	return result;
}
//end

//assign a value to battle
// need to check before call this function
void BattleShip::SetValue(Point p, bool value)
{
	if (!IsInBattle(p)) return;
	int x = p.first;
	int y = p.second;

	//update length tables
	if (!value && m_table[x][y] == -1)
	{
		m_left[x][y] = 0;
		m_right[x][y] = 0;
		m_top[x][y] = 0;
		m_bottom[x][y] = 0;

		int newX, newY;
		//check left
		newY = y;
		while (newY > 0 && m_table[x][newY] != 0)
		{
			newY --;
			if (m_table[x][newY] != 0 && m_right[x][newY] < (y - newY - 1))
			{
				std::cout << "ERROR 102" << std::endl;
			}
			m_right[x][newY] = y - newY - 1;
		}
		//check right
		newY = y;
		while (newY < 9 && m_table[x][newY] != 0)
		{
			newY ++;
			if (m_table[x][newY] != 0 && m_left[x][newY] < (newY - y - 1))
			{
				std::cout << "ERROR 103" << std::endl;
			}
			m_left[x][newY] = newY - y - 1;
		}
		//check top
		newX = x;
		while (newX > 0 && m_table[newX][y] != 0)
		{
			newX --;
			if (m_table[newX][y] != 0 && m_bottom[newX][y] < (x - newX - 1))
			{
				std::cout << "ERROR 104" << std::endl;
			}
			m_bottom[newX][y] = x- newX - 1;
		}
		//check bottom
		newX = x;
		while (newX < 9 && m_table[newX][y] != 0)
		{
			newX ++;
			if (m_table[newX][y] != 0 && m_top[newX][y] < (newX - x -1))
			{
				std::cout << "ERROR 105" << std::endl;
			}
			m_top[newX][y] = newX - x -1;
		}
	}

	m_table[x][y] = value;
}
//end

//find a ship
ShipPtr BattleShip::FindShip(Point p, Ship::TypeOfShip type)
{
	//check condition
	if (!IsInBattle(p) || m_table[p.first][p.second] != 1)
	{
		return ShipPtr();
	}
	//find direction
	Ship::Rotation direction = Ship::HORIZONTAL;
	Point top,bottom,left,right;
	
	top.second = bottom.second = p.second;
	top.first = p.first - 1;
	bottom.first = p.first + 1;

	left.first = right.first = p.first;
	left.second = p.second - 1;
	right.second = p.second + 1;

	if ((IsInBattle(top) && m_table[top.first][top.second] == 1)
		|| (IsInBattle(bottom) && m_table[bottom.first][bottom.second] == 1))
	{
		direction = Ship::VERTICAL;
	}

	//find start position
	int dx, dy;
	if (direction == Ship::HORIZONTAL)
	{
		dx = 0;
		dy = -1;
	}
	else
	{
		dx = -1;
		dy = 0;
	}

	Point newPoint = p;
	do {
		p = newPoint;
		newPoint.first = p.first + dx;
		newPoint.second = p.second + dy;
	} while (IsInBattle(newPoint) && (m_table[newPoint.first][newPoint.second] == 1));
	//we got ship
	m_hasShip[type] = true;
	/*
	for (int i = Ship::AIRCRAFT; i>=Ship::DESTROYER; i--)
	{
		if (!m_hasShip[i])
		{
			m_maxLength = Ship::GetLength((Ship::TypeOfShip)i);
			break;
		}
	}
	*/

	return ShipPtr(new Ship(type,p.first,p.second,direction));

}
//end

//check unknown
bool BattleShip::IsUnkown(Point p)
{
	if (!IsInBattle(p)) return false;
	return m_table[p.first][p.second] == -1;
}

float BattleShip::GetReduceCost(Point p)
{
	if (!IsUnkown(p))
	{
		std::cout << "ERROR 102" << std::endl;
		return 0;
	}
	Point top, bottom, left, right;
	top = bottom = left = right = p;
	// find top
	while (IsUnkown(top))
	{
		top.first --;
	}

	//find bottom
	while (IsUnkown(bottom))
	{
		bottom.first++;
	}

	//find left
	while (IsUnkown(left))
	{
		left.second --;
	}

	//find right
	while (IsUnkown(right))
	{
		right.second ++;
	}

	int height = bottom.first - top.first - 1;
	int width = right.second - left.second - 1;
	float value1 = (bottom.first - p.first) * (p.first - top.first);
	float value2 = (right.second - p.second) * (p.second - left.second);
	if (height < m_maxLength) height += 10;
	if (width < m_maxLength) width += 10;
	return value1 / height + value2 / width;
}

//check max length here
bool BattleShip::IsExist(Point p, int length)
{

	int height = m_top[p.first][p.second] + m_bottom[p.first][p.second] + 1;
	int width = m_left[p.first][p.second] + m_right[p.first][p.second] + 1;
	return (height >= length) || (width >= length);
}//end

//check max length in a rotation
bool BattleShip::IsExist(Point p, Ship::Rotation r, int maxLength)
{
	int length = 0;
	if (r == Ship::HORIZONTAL)
	{
		length =  m_left[p.first][p.second] + m_right[p.first][p.second] + 1;
	}
	else
	{
		length = m_top[p.first][p.second] + m_bottom[p.first][p.second] + 1;
	}
	return length >= maxLength;
}//end
